/******************************************************************************************
文件名：connect_manager.c
作者：狼狼    版本：V1.0    创建日期：2021.9.3
文件描述：
    TCP客户端连接会话和连接管理。
历史修改记录：
1. 2021-9-3:V1.0 狼狼
创建。
2. 2022-5-11:V1.0 狼狼
代码公开。
******************************************************************************************/
//+------------------------------------------------------------------------------------------+
//|                                          文件说明
//+------------------------------------------------------------------------------------------+
//|      连接的创建和连接管理之间是强相关的，连接本身正常/异常退出和主动/被动断开的处理动作是
//|  相互交织的，因此将TCP连接对象的创建和销毁与连接管理的方法放在一个文件中实现，它们在此工程
//|  示例中是绑定在一起的。
//+------------------------------------------------------------------------------------------+
//|                                         头文件包含
//+------------------------------------------------------------------------------------------+
/*|*/#include <stdio.h>
/*|*/#include <string.h>
/*|*/#include <stdlib.h>
/*|*/#include <stdint.h>
/*|*/#include <unistd.h>
/*|*/#include <signal.h>
/*|*/#include <errno.h>
/*|*/#include <linux/sockios.h>
/*|*/#include <sys/ioctl.h>
/*|*/#include <sys/socket.h>
/*|*/#include <netinet/in.h>
/*|*/#include <arpa/inet.h>
//|
/*|*/#include "connect_manager.h"
/*|*/#include "project_debug.h"
//+------------------------------------------------------------------------------------------+
//|                                          全局变量
//+------------------------------------------------------------------------------------------+
/*|*/static uint32_t session_base_id = 1000;
//+------------------------------------------------------------------------------------------+
//|  函数名称：send_package
//|  功能描述：向指定会话的对端发送数据
//|  参数说明：指向连接管理实例的指针，会话ID，数据地址，数据长度
//|  返回值说明：成功返回CONN_SUCCESS，失败返回错误码（负值）
//|  备注：为确保成功，且考虑到Loice Master端发送需求和send操作的线程安全性，此函数要求一次发
//|        送长度需不大于当前缓冲区剩余空间。
//+------------------------------------------------------------------------------------------+
int send_package(struct connect_manager * const pconnman_obj, uint32_t session_id, const uint8_t *pdata, uint32_t data_len)
{
    int i = 0;
    int ret = 0;
    
    /* 参数检查 */
    if(pconnman_obj == NULL)
    {
        OUTPUT("[CONNMAN-ERROR]Pointer is NULL!\n");
        return CONN_ERROR_ILLEGAL_PARAM;
    }
    if(pconnman_obj->state != CONN_MANAGER_STAT_OK)
    {
        OUTPUT("[CONNMAN-ERROR]The current state[%d] does not support this operation[send_package]!\n", pconnman_obj->state);
        return CONN_ERROR_UNEXPECT_STATE;
    }
    
    /* 在记录表中查找并匹配对应的会话 */
    for(i = 0; i < MAX_CONNECT_NUM; i++)
    {
        if(pconnman_obj->connection_list[i] != NULL)
        {
            if(pconnman_obj->connection_list[i]->session_id == session_id)
            {
                break;/* 此时i值为连接在记录表中的位置 */
            }
        }
    }
    if(i >= MAX_CONNECT_NUM)
    {
        OUTPUT("[CONNMAN-ERROR]Can't find session in connection list!\n");
        return CONN_ERROR_SESSION_NON_EXISTENT;
    }
    
    pthread_mutex_lock(&pconnman_obj->connection_list[i]->tx_mutex);
    
    /* 验证会话状态 */
    if(pconnman_obj->connection_list[i]->state != TCP_CONN_STAT_OK)
    {
        OUTPUT("[CONNMAN-ERROR]The connection state[%d] does not support this operation[send_package]!\n", pconnman_obj->connection_list[i]->state);
        pthread_mutex_unlock(&pconnman_obj->connection_list[i]->tx_mutex);
        return CONN_ERROR_UNEXPECT_STATE;
    }
    
    /* 发送缓冲区剩余空间检查 */
    int tcp_tx_space_total  = 0;
    int tcp_tx_space_used   = 0;
    socklen_t optlen = sizeof(int);
    ret = getsockopt(pconnman_obj->connection_list[i]->conn_sock, SOL_SOCKET, SO_SNDBUF, &tcp_tx_space_total, &optlen);
    if(ret < 0)
    {
        pthread_mutex_unlock(&pconnman_obj->connection_list[i]->tx_mutex);
        OUTPUT("[CONNMAN-ERROR]Get TCP send buf size failed!\n");
        return CONN_ERROR_SOCKET;
    }
    ret = ioctl(pconnman_obj->connection_list[i]->conn_sock, SIOCOUTQ, &tcp_tx_space_used);
    if(ret < 0)
    {
        pthread_mutex_unlock(&pconnman_obj->connection_list[i]->tx_mutex);
        OUTPUT("[CONNMAN-ERROR]Get TCP send buf used size failed!\n");
        return CONN_ERROR_SOCKET;
    }
    if(tcp_tx_space_total - tcp_tx_space_used < data_len)
    {
        pthread_mutex_unlock(&pconnman_obj->connection_list[i]->tx_mutex);
        OUTPUT("[CONNMAN-ERROR]TCP send buf is not enough[%d < %d]!\n", tcp_tx_space_total - tcp_tx_space_used, data_len);
        return CONN_ERROR_SOCKET;
    }
    
    /* 数据发送 */
    if(send(pconnman_obj->connection_list[i]->conn_sock, pdata, data_len, MSG_NOSIGNAL) < 0)
    {
        pthread_mutex_unlock(&pconnman_obj->connection_list[i]->tx_mutex);
        return CONN_ERROR_SOCKET;
    }
    
    pthread_mutex_unlock(&pconnman_obj->connection_list[i]->tx_mutex);
    
    return CONN_SUCCESS;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：tcp_client_create
//|  功能描述：向远程TCP服务器发起连接
//|  参数说明：远端IP地址，远端端口号
//|  返回值说明：成功返回Socket描述符，失败返回错误码（负值）
//|  备注：TO DO 实现非阻塞connect
//+------------------------------------------------------------------------------------------+
int tcp_client_create(const char *remote_ip_address, int port)
{
    int tcp_socket = 0;
    struct sockaddr_in ip_address;
    
    /* 参数检查 */
    if(port <= 0)
    {
        OUTPUT("[CONNMAN-ERROR]The port number[%d] must be a positive integer!\n", port);
        return CONN_ERROR_ILLEGAL_PARAM;
    }
    if(remote_ip_address == NULL)
    {
        OUTPUT("[CONNMAN-ERROR]The ip address is NULL!\n");
        return CONN_ERROR_ILLEGAL_PARAM;
    }
    
    /* 创建套接字并使用非阻塞方式连接 */
    tcp_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(tcp_socket < 0)
    {
        OUTPUT("[CONNMAN-ERROR]Open socket failed!\n");
        perror("[Error Details]");
        return CONN_ERROR_SOCKET;
    }
    memset(&ip_address, 0, sizeof(ip_address));
    ip_address.sin_family = PF_INET;
	inet_aton(remote_ip_address, &ip_address.sin_addr);
	ip_address.sin_port = htons(port);
    
    // TO DO: 使用非阻塞方式（自定义超时时间）执行connect动作
    
    // only for test NOW!
    
    //setsockopt(tcp_socket,SOL_SOCKET,SO_SNDTIMEO,&timeover,sizeof(timeover));//connect操作受发送超时的影响
    if(connect(tcp_socket, (struct sockaddr *)&ip_address, sizeof(ip_address)) != 0)
    {
        close(tcp_socket);
        return CONN_ERROR_SOCKET;
    }
    
    return tcp_socket;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：add_connect
//|  功能描述：根据Socket描述符，向连接管理中增加一个会话
//|  参数说明：指向连接管理实例的指针，Socket描述符，会话ID输出指针
//|  返回值说明：成功返回CONN_SUCCESS，psession_id输出分配的会话ID，失败返回错误码（负值）
//|  备注：使用此接口添加的会话需要使用start_recv接口使能接收，中间时间差可供调用程序准备所需。
//+------------------------------------------------------------------------------------------+
static int add_connect(struct connect_manager * const pconnman_obj,
                       int tcp_client_sock,
                       uint32_t *psession_id)
{
    struct sockaddr_in local_ip_addr;
    struct sockaddr_in remote_ip_addr;
    socklen_t addr_len = (socklen_t)sizeof(struct sockaddr_in);
    
    /* 参数检查 */
    if(pconnman_obj == NULL)
    {
        OUTPUT("[CONNMAN-ERROR]Pointer is NULL!\n");
        return CONN_ERROR_ILLEGAL_PARAM;
    }
    if(pconnman_obj->state != CONN_MANAGER_STAT_OK)
    {
        OUTPUT("[CONNMAN-ERROR]The current state[%d] does not support this operation[add_connect]!\n", pconnman_obj->state);
        return CONN_ERROR_UNEXPECT_STATE;
    }
    if(tcp_client_sock < 0)
    {
        OUTPUT("[CONNMAN-ERROR]Illegal socket descriptor!\n");
        return CONN_ERROR_ILLEGAL_PARAM;
    }
    if(psession_id == NULL)
    {
        OUTPUT("[CONNMAN-ERROR]Session ID output pointer is NULL!\n");
        return CONN_ERROR_ILLEGAL_PARAM;
    }
    
    /* 检查连接管理是否达到管理上限 */
    if(pconnman_obj->connection_num >= MAX_CONNECT_NUM)
    {
        OUTPUT("[CONNMAN-ERROR]Connection list is full!\n");
        return CONN_ERROR_SESSION_LIST_FULL;
    }
    
    /* 检查连接是否被重复添加 */
    for(int i = 0; i < MAX_CONNECT_NUM; i++)
    {
        if(pconnman_obj->connection_list[i] != NULL)
        {
            if(pconnman_obj->connection_list[i]->conn_sock == tcp_client_sock)
            {
                OUTPUT("[CONNMAN-ERROR]The socket[sock=%d] has been registered!\n", tcp_client_sock);
                return CONN_ERROR_SESSION_REGISTERED;
            }
        }
    }
    
    /* 创建TCP_CONN对象 */
    ST_TCP_CONN *ptmp = (ST_TCP_CONN *)calloc(1, sizeof(ST_TCP_CONN));
    if(ptmp == NULL)
    {
        OUTPUT("[CONNMAN-ERROR]Malloc[%ld] failed!\n", sizeof(ST_TCP_CONN));
        return CONN_ERROR_MALLOC;
    }
    
    /* 更新会话对象结构 */
    pthread_mutex_init(&ptmp->tx_mutex, NULL);
    ptmp->conn_sock = tcp_client_sock;
    
    getsockname(ptmp->conn_sock, (struct sockaddr *)&local_ip_addr,  &addr_len);
    getpeername(ptmp->conn_sock, (struct sockaddr *)&remote_ip_addr, &addr_len);
    inet_ntop(AF_INET, &local_ip_addr.sin_addr,  ptmp->local_ip_address,  sizeof(ptmp->local_ip_address));
    inet_ntop(AF_INET, &remote_ip_addr.sin_addr, ptmp->remote_ip_address, sizeof(ptmp->remote_ip_address));
    ptmp->local_ip_port  = ntohs(local_ip_addr.sin_port);
    ptmp->remote_ip_port = ntohs(remote_ip_addr.sin_port);
    ptmp->state          = TCP_CONN_STAT_STANDBY;
    ptmp->session_id     = session_base_id++;
    
    /* 将会话添加到内部记录 */
    // ----------------------------------------
    // [说明]前面已经判断列表是否已满，此处无需
    //       再次判断。
    // ----------------------------------------
    for(int i = 0; i < MAX_CONNECT_NUM; i++)
    {
        if(pconnman_obj->connection_list[i] == NULL)
        {
            pconnman_obj->connection_list[i] = ptmp;
            pconnman_obj->connection_num++;
            OUTPUT("[CONNMAN-INFO]Add session[%d] to connection list[%d]!\n", ptmp->session_id, i);
            break;
        }
    }
    
    *psession_id = ptmp->session_id;
    
    return CONN_SUCCESS;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：start_recv
//|  功能描述：改变会话的状态，以启动对其数据的接收
//|  参数说明：指向连接管理实例的指针，会话ID
//|  返回值说明：成功返回CONN_SUCCESS，失败返回错误码（负值）
//|  备注：此接口的存在为上级应用准备后级模块提供了必要的时间，当完成接收准备后，可以通过该接
//|        口使能接收，此后该连接将参与可读判断的select轮询。
//+------------------------------------------------------------------------------------------+
static int start_recv(struct connect_manager * const pconnman_obj, uint32_t session_id)
{
    /* 参数检查 */
    if(pconnman_obj == NULL)
    {
        OUTPUT("[CONNMAN-ERROR]Pointer is NULL!\n");
        return CONN_ERROR_ILLEGAL_PARAM;
    }
    
    /* 在记录表中查找并匹配对应的会话 & 修改会话状态 */
    for(int i = 0; i < MAX_CONNECT_NUM; i++)
    {
        if(pconnman_obj->connection_list[i] != NULL)
        {
            if(pconnman_obj->connection_list[i]->session_id == session_id)
            {
                pconnman_obj->connection_list[i]->state = TCP_CONN_STAT_OK;
                return CONN_SUCCESS;
            }
        }
    }
    
    return CONN_ERROR_SESSION_NON_EXISTENT;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：delete_connect
//|  功能描述：从连接管理中删除一个指定记录位置的TCP连接
//|  参数说明：指向连接管理实例的指针，会话ID
//|  返回值说明：成功返回CONN_SUCCESS，失败返回错误码（负值）
//|  备注：该函数处理过程不考虑互斥问题，仅供内部调用！
//+------------------------------------------------------------------------------------------+
static int delete_connect(struct connect_manager * const pconnman_obj, uint32_t index)
{
    /* 参数检查 */
    if(pconnman_obj == NULL)
    {
        OUTPUT("[CONNMAN-ERROR]Pointer is NULL!\n");
        return CONN_ERROR_ILLEGAL_PARAM;
    }
    
    if(pconnman_obj->connection_list[index] == NULL)
    {
        OUTPUT("[CONNMAN-ERROR]Can't find session in connection list[%d]!\n", index);
        return CONN_ERROR_SESSION_NON_EXISTENT;
    }
    
    /* 切换连接运行状态 */
    pthread_mutex_lock(&pconnman_obj->connection_list[index]->tx_mutex);
    pconnman_obj->connection_list[index]->state = TCP_CONN_STAT_END;
    pthread_mutex_unlock(&pconnman_obj->connection_list[index]->tx_mutex);
    
    /* 从记录表中删除对该连接的记录 */
    close(pconnman_obj->connection_list[index]->conn_sock);
    pthread_mutex_destroy(&pconnman_obj->connection_list[index]->tx_mutex);
    ST_TCP_CONN *ptmp = pconnman_obj->connection_list[index];
    pconnman_obj->connection_list[index] = NULL;
    pconnman_obj->connection_num--;
    free(ptmp);
    
    
    OUTPUT("[CONNMAN-INFO]Delete session from connection list[%d]!\n", index);
    
    return CONN_SUCCESS;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：pre_delete_connect
//|  功能描述：从连接管理中预删除一个TCP连接
//|  参数说明：指向连接管理实例的指针，会话ID
//|  返回值说明：成功返回CONN_SUCCESS，失败返回错误码（负值）
//|  备注：查找会话，将其状态修改标记为待删除，当接收线程下一次处理到此连接时，将清除对该连接
//|        的记录。
//+------------------------------------------------------------------------------------------+
static int pre_delete_connect(struct connect_manager * const pconnman_obj, uint32_t session_id)
{
    /* 参数检查 */
    if(pconnman_obj == NULL)
    {
        OUTPUT("[CONNMAN-ERROR]Pointer is NULL!\n");
        return CONN_ERROR_ILLEGAL_PARAM;
    }
    
    /* 在记录表中查找并匹配对应的会话 & 修改会话状态 */
    for(int i = 0; i < MAX_CONNECT_NUM; i++)
    {
        if(pconnman_obj->connection_list[i] != NULL)
        {
            if(pconnman_obj->connection_list[i]->session_id == session_id)
            {
                pconnman_obj->connection_list[i]->state = TCP_CONN_STAT_END;
                return CONN_SUCCESS;
            }
        }
    }
    
    return CONN_ERROR_SESSION_NON_EXISTENT;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：recv_process
//|  功能描述：连接管理接收处理线程，用于轮询接收各TCP连接传来的数据
//|  参数说明：指向连接管理实例的指针
//|  返回值说明：无
//|  备注：
//+------------------------------------------------------------------------------------------+
static void *recv_process(void *p)
{
    int ret = 0;
    
    ST_CONNECT_MANAGER *pconnman_obj = (ST_CONNECT_MANAGER *)p;
    
    fd_set conn_sock_readfds;
    int max_fd = 0;
    struct timeval timeoutval;
    
    while(pconnman_obj->flag_run)
    {
        FD_ZERO(&conn_sock_readfds);
        max_fd = 0;
        
        /* 将标记为预删除的会话从记录中清除 & 释放其所占的资源 */
        for(int i = 0; i < MAX_CONNECT_NUM; i++)
        {
            if(pconnman_obj->connection_list[i] != NULL)
            {
                if(pconnman_obj->connection_list[i]->state == TCP_CONN_STAT_END)
                {
                    /* 调用内部删除函数，该方法不考虑互斥而是直接查找并删除对应的记录 */
                    delete_connect(pconnman_obj, i);
                }
            }
        }
        
        /* 遍历连接记录表，获取所有待轮询连接描述符 */
        for(int i = 0; i < MAX_CONNECT_NUM; i++)
        {
            if(pconnman_obj->connection_list[i] != NULL && pconnman_obj->connection_list[i]->state == TCP_CONN_STAT_OK)
            {
                FD_SET(pconnman_obj->connection_list[i]->conn_sock, &conn_sock_readfds);
                if(pconnman_obj->connection_list[i]->conn_sock > max_fd)
                {
                    max_fd = pconnman_obj->connection_list[i]->conn_sock;
                }
            }
        }
        
        /* 列表为空的动作 */
        if(max_fd == 0)
        {
            sleep(1);
            continue;
        }
        
        /* 对会话执行select轮询 */
        timeoutval.tv_sec  = 1;
        timeoutval.tv_usec = 0;
        ret = select(max_fd + 1, &conn_sock_readfds, NULL, NULL, &timeoutval);
        if(ret < 0)
        {
            OUTPUT("[CONNMAN-ERROR]Select error!\n");
            pconnman_obj->exit_state = CONN_EXIT_STAT_SELECT_ERROR;
            break;
        }
        else if(ret == 0)
        {
            continue;
        }
        else
        {
            for(int i = 0; i < MAX_CONNECT_NUM; i++)
            {
                if(pconnman_obj->connection_list[i] != NULL)
                {
                    if(FD_ISSET(pconnman_obj->connection_list[i]->conn_sock, &conn_sock_readfds))
                    {
                        if(pconnman_obj->recv_handle != NULL)
                        {
                            
                            /* 调用该连接的接收回调 */
                            ret = pconnman_obj->recv_handle(pconnman_obj->connection_list[i]->conn_sock, pconnman_obj->connection_list[i]->session_id);
                            
                            /* 对回调结果做处理 */
                            switch(ret)
                            {
                                case CONN_EXIT_STAT_RX_ERROR:
                                case CONN_ERROR_SESSION_NON_EXISTENT:
                                case CONN_EXIT_STAT_DISCONNECT_OR_CLOSE:
                                    // ----------------------------------------
                                    // [说明]当出现如接收异常、对端关闭等情况，
                                    //       将会话状态标记为<结束>，由预删除执
                                    //       行逻辑从记录表中清除该连接。
                                    // ----------------------------------------
                                    pconnman_obj->connection_list[i]->state = TCP_CONN_STAT_END;
                                break;
                                default:break;
                            }
                        }
                    }
                }
            }
        }
    }
    
    pconnman_obj->state = CONN_MANAGER_STAT_EXIT;
    
    pthread_exit(NULL);
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：connect_manager_create
//|  功能描述：创建一个连接管理实例
//|  参数说明：用于输出连接管理对象结构的指针
//|  返回值说明：成功返回CONN_SUCCESS，ppconnman_obj指针输出指向对象结构体的空间，失败返回错误码（负值）
//|  备注：
//+------------------------------------------------------------------------------------------+
int connect_manager_create(ST_CONNECT_MANAGER **ppconnman_obj, int (*recv_handle)(int conn_sock, uint32_t session_id))
{
    /* 参数检查 */
    if(ppconnman_obj == NULL)
    {
        OUTPUT("[CONNMAN-ERROR]Level 2 pointer can't be NULL!\n");
        return CONN_ERROR_ILLEGAL_PARAM;
    }
    if(*ppconnman_obj != NULL)
    {
        OUTPUT("[CONNMAN-ERROR]Level 1 pointer mast be NULL!\n");
        return CONN_ERROR_ILLEGAL_PARAM;
    }
    if(recv_handle == NULL)
    {
        OUTPUT("[CONNMAN-ERROR]Recv handle is NULL!\n");
        return CONN_ERROR_ILLEGAL_PARAM;
    }
    
    /* 申请连接管理对象结构空间 */
    ST_CONNECT_MANAGER *ptmp = (ST_CONNECT_MANAGER *)calloc(1, sizeof(ST_CONNECT_MANAGER));
    if(ptmp == NULL)
    {
        OUTPUT("[CONNMAN-ERROR]Malloc[%ld] failed!\n", sizeof(ST_CONNECT_MANAGER));
        return CONN_ERROR_MALLOC;
    }
    
    /* 创建管理线程和数据接收线程 */
    ptmp->flag_run = true;
    if(pthread_create(&ptmp->recv_tid, NULL, recv_process, (void *)ptmp) != 0)
    {
        OUTPUT("[CONNMAN-ERROR]Create recv process thread failed!\n");
        free(ptmp);
        return CONN_ERROR_THREAD;
    }
    
    /* 更新连接管理对象结构 */
    ptmp->add_connect        = add_connect;
    ptmp->start_recv         = start_recv;
    ptmp->pre_delete_connect = pre_delete_connect;
    ptmp->recv_handle        = recv_handle;
    ptmp->send_package       = send_package;
    ptmp->state              = CONN_MANAGER_STAT_OK;
    
    *ppconnman_obj = ptmp;
    
    return CONN_SUCCESS;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：connect_manager_destroy
//|  功能描述：销毁一个连接管理实例，释放其所占的资源
//|  参数说明：指向连接管理实例索引指针的指针
//|  返回值说明：成功返回CONN_SUCCESS，失败返回错误码（负值）
//|  备注：若执行成功，原指针的值会被置为NULL。
//+------------------------------------------------------------------------------------------+
int connect_manager_destroy(ST_CONNECT_MANAGER **ppconnman_obj)
{
    /* 参数检查 */
    if(ppconnman_obj == NULL)
    {
        OUTPUT("[CONNMAN-ERROR]Level 2 pointer can't be NULL!\n");
        return CONN_ERROR_ILLEGAL_PARAM;
    }
    if(*ppconnman_obj == NULL)
    {
        OUTPUT("[CONNMAN-ERROR]Level 1 pointer can't be NULL!\n");
        return CONN_ERROR_ILLEGAL_PARAM;
    }
    if((*ppconnman_obj)->state != CONN_MANAGER_STAT_OK && 
       (*ppconnman_obj)->state != CONN_MANAGER_STAT_EXIT)
    {
        OUTPUT("[CONNMAN-ERROR]The current state[%d] does not support this operation[destroy]!\n", (*ppconnman_obj)->state);
        return CONN_ERROR_UNEXPECT_STATE;
    }
    
    /* 停止对各连接的接收处理 */
    (*ppconnman_obj)->flag_run = false;
    pthread_join((*ppconnman_obj)->recv_tid, NULL);
    
    /* 遍历并清空向后级模块注册的连接，关闭并释放连接管理持有的所有会话 */
    for(int i = 0; i < MAX_CONNECT_NUM; i++)
    {
        if((*ppconnman_obj)->connection_list[i] != NULL)
        {
            /* 调用内部删除函数，该方法不考虑互斥而是直接查找并删除对应的记录 */
            delete_connect(*ppconnman_obj, i);
        }
    }
    
    /* 释放连接管理本身所占资源 */
    free(*ppconnman_obj);
    *ppconnman_obj = NULL;
    
    return CONN_SUCCESS;
}
